﻿using System;
using System.Collections.Generic;
using System.Linq;
using ITSP.SharePoint.CacheRefresh.Common;
using ITSP.SharePoint.CacheRefresh.Interfaces;
using ITSP.SharePoint.CacheRefresh.Repositories;
using ITSP.SharePoint.CacheRefresh.SpecialCases;
using ITSP.SharePoint.CacheRefresh.TimerJobs;
using Microsoft.Practices.SharePoint.Common.Logging;
using Microsoft.Practices.SharePoint.Common.ServiceLocation;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;

namespace ITSP.SharePoint.CacheRefresh
{
    /// <summary>
    /// The Cache Refresh Manager class.
    /// Use to manage the Cache Refresh Configuration and components.
    /// </summary>
    /// <createdby>Simon Doy</createdby>
    public class CacheRefreshManager : ICacheRefreshManager
    {
        private ILogger _logger = SharePointServiceLocator.GetCurrent().GetInstance<ILogger>();
        private string _hostWebApplicationUrl = "";
        private ICacheConfigurationRepository _repository = null;

        #region Constructors

        /// <summary>
        /// Default Constructor: used for Serialization.
        /// </summary>
        public CacheRefreshManager()
        {
            
        }

        /// <summary>
        /// Used to initialse the Cache Manager using a <see cref="SPWebApplication"/> object.
        /// </summary>
        /// <param name="hostWebApplication">The Web Application that is hosting the Cache Configuration List.</param>
        public CacheRefreshManager(SPWebApplication hostWebApplication)
        {
            SPSite rootSite = hostWebApplication.Sites.FirstOrDefault();
            if(rootSite==null)
            {
                throw new SPException("Unable to find Cache Configuration list in Web Application, has a site collection been created?");
            }
            _hostWebApplicationUrl = rootSite.Url;
            _repository = new SPCacheConfigurationRepository(_hostWebApplicationUrl);
        }

        /// <summary>
        /// Used to initialise the Cache Manager using a string, this expected to be the url of a Web Application
        /// </summary>
        /// <param name="hostWebApplicationUrl">The root webapplication url (e.g http://sharepoint)</param>
        public CacheRefreshManager(string hostWebApplicationUrl)
        {
            _hostWebApplicationUrl = hostWebApplicationUrl;
            _repository = new SPCacheConfigurationRepository(hostWebApplicationUrl);
        }

        #endregion

        /// <summary>
        /// Gets a Cache Configuration Item
        /// </summary>
        /// <param name="cacheKey">The key to look for the Cache Configuration Item with.</param>
        /// <returns>The Cache Configuration Item</returns>
        public ICacheConfigItem GetCache(string cacheKey)
        {
            _logger.TraceToDeveloper(String.Format("CacheRefreshManager::GetCache(Cache Key: {0}) Enter", cacheKey), 0, TraceSeverity.Verbose, Constants.Logging.DefaultLoggingCategory);
            ICacheConfigItem item = new SPCacheConfigItemNotFound();

            try
            {
                item = _repository.Get(cacheKey);
            }
            catch (Exception ex)
            {
                _logger.TraceToDeveloper(ex, 0, TraceSeverity.Unexpected, Constants.Logging.DefaultLoggingCategory);
                throw;
            }

            _logger.TraceToDeveloper(String.Format("CacheRefreshManager::GetCache(Exists: {0}) Exit", item.Exists), 0, TraceSeverity.Verbose, Constants.Logging.DefaultLoggingCategory);
            return item;
        }

        /// <summary>
        /// Add a Cache Configuration Item
        /// </summary>
        /// <param name="item">The Cache Configuration Item to add to the Cache Configuration List</param>
        /// <returns>True/False depending on the success of the operation.</returns>
        public bool AddCache(ICacheConfigItem item)
        {
            _logger.TraceToDeveloper(String.Format("CacheRefreshManager::AddCache(Cache Key: {0}) Enter", item.CacheKey), 0, TraceSeverity.Verbose, Constants.Logging.DefaultLoggingCategory);
            bool bSuccess = false;

            try
            {
                ICacheConfigItem checkListItem = _repository.Add(item.CacheKey, item.Description, item.CacheDependencyPath, item.CacheFrequency, item.CacheEnabled);
                if(checkListItem!=null && checkListItem.Exists)
                {
                    bSuccess = true;
                }
            }
            catch (Exception ex)
            {
                _logger.TraceToDeveloper(ex, 0, TraceSeverity.Unexpected, Constants.Logging.DefaultLoggingCategory);
                throw;
            }
           
            _logger.TraceToDeveloper(String.Format("CacheRefreshManager::AddCache(Success: {0}) Exit", bSuccess), 0, TraceSeverity.Verbose, Constants.Logging.DefaultLoggingCategory);
            return bSuccess;
        }

        /// <summary>
        /// Deletes a Cache Configuration Item
        /// </summary>
        /// <param name="item">The Cache Configuration Item to delete from the Cache Configuration List</param>
        /// <returns>True/False depending on the success of the operation.</returns>
        public bool DeleteCache(ICacheConfigItem item)
        {
            _logger.TraceToDeveloper(String.Format("CacheRefreshManager::DeleteCache(Cache Key: {0}) Enter", item.CacheKey), 0, TraceSeverity.Verbose, Constants.Logging.DefaultLoggingCategory);
            bool bSuccess = false;

            try
            {
                bSuccess = _repository.Delete(item.CacheKey);
            }
            catch (Exception ex)
            {
                _logger.TraceToDeveloper(ex, 0, TraceSeverity.Unexpected, Constants.Logging.DefaultLoggingCategory);
                throw;
            }

            _logger.TraceToDeveloper(String.Format("CacheRefreshManager::DeleteCache(Success: {0}) Exit", bSuccess), 0, TraceSeverity.Verbose, Constants.Logging.DefaultLoggingCategory);
            return bSuccess;
        }

        /// <summary>
        /// Updates a Cache Configuration Item in the Cache Confiuration List.
        /// </summary>
        /// <param name="item">The Cache Configuration Item to update the Cache Configuration List</param>
        /// <returns>True/False depending on the success of the operation.</returns>
        public bool UpdateCache(ICacheConfigItem item)
        {
            _logger.TraceToDeveloper(String.Format("CacheRefreshManager::UpdateCache(Cache Key: {0}) Enter", item.CacheKey), 0, TraceSeverity.Verbose, Constants.Logging.DefaultLoggingCategory);
            bool bSuccess = false;

            try
            {
                ICacheConfigItem checkListItem = _repository.Update(item);
                if (checkListItem != null && checkListItem.Exists)
                {
                    bSuccess = true;
                }
            }
            catch (Exception ex)
            {
                _logger.TraceToDeveloper(ex, 0, TraceSeverity.Unexpected, Constants.Logging.DefaultLoggingCategory);
                throw;
            }

            _logger.TraceToDeveloper(String.Format("CacheRefreshManager::UpdateCache(Success: {0}) Exit", bSuccess), 0, TraceSeverity.Verbose, Constants.Logging.DefaultLoggingCategory);
            return bSuccess;
        }

        /// <summary>
        /// Used to get a Cache Dependency object with the Cache Key
        /// </summary>
        /// <param name="cacheKey">the value of the item that is in the Cache Configuration List.</param>
        /// <returns>A Cache Dependency Object which can be used with <see cref="System.Web.Caching.Cache"/>.</returns>
        public SPCacheFileDependency GetCacheDependency(string cacheKey)
        {
            _logger.TraceToDeveloper(String.Format("CacheRefreshManager::GetCacheDependency(Cache Key: {0}) Enter", cacheKey), 0, TraceSeverity.Verbose, Constants.Logging.DefaultLoggingCategory);
            SPCacheFileDependency dependency=new InvalidSPCacheFileDependency();

            try
            {
                ICacheConfigItem checkListItem = _repository.Get(cacheKey);
                if (checkListItem != null && checkListItem.Exists)
                {
                    _logger.TraceToDeveloper(String.Format("Found Cache Dependency with {0}", cacheKey), 0, TraceSeverity.Verbose,Constants.Logging.DefaultLoggingCategory);
                    dependency = new SPCacheFileDependency(checkListItem);
                }
            }
            catch (Exception ex)
            {
                _logger.TraceToDeveloper(ex, 0, TraceSeverity.Unexpected, Constants.Logging.DefaultLoggingCategory);
                throw;
            }

            _logger.TraceToDeveloper(String.Format("CacheRefreshManager::GetCacheDependency(Exists: {0}) Exit", dependency.Exists), 0, TraceSeverity.Verbose, Constants.Logging.DefaultLoggingCategory);
            return dependency;
        }

        /// <summary>
        /// Invalidate the Cache. This uses a global timer job which runs on each server in the farm.
        /// </summary>
        /// <param name="cacheKey">The Cache key that is associated to the Cache objects.</param>
        /// <returns>True/False depending on the success of the operation.</returns>
        public bool InvalidateCache(string cacheKey)
        {
            _logger.TraceToDeveloper(String.Format("CacheRefreshManager::InvalidateCache(Cache Key: {0}) Enter", cacheKey), 0, TraceSeverity.Verbose, Constants.Logging.DefaultLoggingCategory);
            bool bSuccess = false;

            try
            {

                SPService timerService = SPFarm.Local.TimerService;

                SPListItem checkListItem = _repository.GetListItem(cacheKey);
                if (checkListItem != null)
                {
                    InvalidateCacheFileJobDefinition.CreateOneTime(checkListItem, timerService);
                    bSuccess = true;
                }
            }
            catch (Exception ex)
            {
                _logger.TraceToDeveloper(ex, 0, TraceSeverity.Unexpected, Constants.Logging.DefaultLoggingCategory);
                throw;
            }
           
            _logger.TraceToDeveloper(String.Format("CacheRefreshManager::InvalidateCache(Success: {0}) Exit", bSuccess), 0, TraceSeverity.Verbose, Constants.Logging.DefaultLoggingCategory);
            return bSuccess;
        }

        /// <summary>
        /// This function causes all the timer jobs to be deleted and readded based on the configuration of the Cache Configuration list.
        /// </summary>
        /// <returns>True/False depending on the success of the operation.</returns>
        public bool RebuildCacheTimerJobs()
        {
            bool bSuccess = false;
            try
            {
                
                SPService timerService = SPFarm.Local.TimerService;
                if (InvalidateCacheFileJobDefinition.DeleteAllJobs())
                {
                    IList<ICacheConfigItem> listOfItems = _repository.GetAll();
                    foreach (ICacheConfigItem item in listOfItems)
                    {
                            
                        SPListItem listItem = _repository.GetListItem(item.CacheKey);
                        InvalidateCacheFileJobDefinition job = InvalidateCacheFileJobDefinition.Create(listItem, timerService);

                    }
                    bSuccess = true;
                }
               
            }
            catch (Exception)
            {
                
                throw;
            }
            return bSuccess;
        }
    }
}
